# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

require 'ostruct'
require 'spaceship'

skip_docs
opt_out_usage

KEY_GRADLE_APK_PATH = "apkPath"
KEY_S3_APK_PATH = "s3APKPath"
KEY_GRADLE_AAB_PATH = "aabPath"
KEY_IPA_PATH = "ipaPath"
KEY_DSYM_PATH = "dsymPath"

# Export environment variables to GITHUB_ENV
# If there's no GITHUB_ENV file set in the env, then this is a no-op
def exportEnvVars(env_vars)
  github_env_path = ENV['GITHUB_ENV']
  if github_env_path && File.exist?(github_env_path)
    puts "Saving environment variables in GITHUB_ENV..."
    File.open(github_env_path, "a") do |file|
      env_vars.each do |key, value|
        puts "#{key}=#{value}"
        file.puts "#{key}=#{value}"
      end
    end
  end
end

def setGradleOutputsInEnv()
  puts "Saving Android build outputs in env..."
  env_vars = {
    KEY_GRADLE_APK_PATH => lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH],
  }
  if lane_context.key?(SharedValues::GRADLE_AAB_OUTPUT_PATH)
    env_vars[KEY_GRADLE_AAB_PATH] = lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
  end
  exportEnvVars(env_vars)
end

def setIOSBuildOutputsInEnv()
  puts "Saving iOS build outputs in env..."
  exportEnvVars({
    KEY_IPA_PATH => lane_context[SharedValues::IPA_OUTPUT_PATH],
    KEY_DSYM_PATH => lane_context[SharedValues::DSYM_OUTPUT_PATH],
  })
end

platform :android do
  desc "Generate a production AAB"
  lane :build do
    ENV["ENVFILE"]=".env.production"
    gradle(
      project_dir: './android',
      task: 'bundle',
      flavor: 'Production',
      build_type: 'Release',
    )
    setGradleOutputsInEnv()
  end

  desc "Generate a production HybridApp AAB"
  lane :build_hybrid do
    ENV["ENVFILE"]="Mobile-Expensify/.env.production.hybridapp.android"
    gradle(
      project_dir: 'Mobile-Expensify/Android',
      task: "bundle",
      build_type: ENV["ANDROID_BUILD_TYPE"] || "Release",
      flags: "--refresh-dependencies",
      properties: {
        "android.injected.signing.store.file" => './upload-key.keystore',
        "android.injected.signing.store.password" => ENV["ANDROID_UPLOAD_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["ANDROID_UPLOAD_KEYSTORE_ALIAS"],
        "android.injected.signing.key.password" => ENV["ANDROID_UPLOAD_KEY_PASSWORD"],
      }
    )
    setGradleOutputsInEnv()
  end

  desc "Generate AdHoc HybridApp apk"
  lane :build_adhoc_hybrid do
    ENV["ENVFILE"]="Mobile-Expensify/.env.adhoc.hybridapp.android"
    gradle(
      project_dir: 'Mobile-Expensify/Android',
      task: 'assembleAdhoc',
      properties: {
        "android.injected.signing.store.file" => './upload-key.keystore',
        "android.injected.signing.store.password" => ENV["ANDROID_UPLOAD_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["ANDROID_UPLOAD_KEYSTORE_ALIAS"],
        "android.injected.signing.key.password" => ENV["ANDROID_UPLOAD_KEY_PASSWORD"],
      }
    )
    setGradleOutputsInEnv()
  end

  desc "Generate a new local APK"
  lane :build_local do
    ENV["ENVFILE"]=".env.production"
    gradle(
      project_dir: './android',
      task: 'assemble',
      flavor: 'Production',
      build_type: 'Release',
    )
    setGradleOutputsInEnv()
  end

  desc "Generate a new local HybridApp APK"
  lane :build_local_hybrid do
    ENV["ENVFILE"]="Mobile-Expensify/.env.production.hybridapp.android"
    gradle(
      project_dir: 'Mobile-Expensify/Android',
      task: 'assemble',
      build_type: 'Debug',
    )
    setGradleOutputsInEnv()
  end

  desc "Generate a new local APK for e2e testing"
  lane :build_e2e do
    ENV["ENVFILE"]="tests/e2e/.env.e2e"
    ENV["ENTRY_FILE"]="src/libs/E2E/reactNativeLaunchingTest.ts"
    ENV["E2E_TESTING"]="true"

    gradle(
      project_dir: './android',
      task: ':app:assemble',
      flavor: 'e2e',
      build_type: 'Release',
    )
    setGradleOutputsInEnv()
  end

  lane :build_e2eDelta do
    ENV["ENVFILE"]="tests/e2e/.env.e2edelta"
    ENV["ENTRY_FILE"]="src/libs/E2E/reactNativeLaunchingTest.ts"
    ENV["E2E_TESTING"]="true"

    gradle(
      project_dir: './android',
      task: ':app:assemble',
      flavor: 'e2edelta',
      build_type: 'Release',
    )
    setGradleOutputsInEnv()
  end

  desc "Build AdHoc testing build"
  lane :build_adhoc do
    ENV["ENVFILE"]=".env.adhoc"
    gradle(
      project_dir: './android',
      task: 'assemble',
      flavor: 'Adhoc',
      build_type: 'Release',
    )
    setGradleOutputsInEnv()
  end

  desc "Upload build to S3"
  lane :upload_s3 do
    puts "APK path: #{ENV[KEY_GRADLE_APK_PATH]}"
    aws_s3(
      access_key: ENV['S3_ACCESS_KEY'],
      secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
      bucket: ENV['S3_BUCKET'],
      region: ENV['S3_REGION'],
      apk: ENV[KEY_GRADLE_APK_PATH],
      app_directory: "android/#{ENV['PULL_REQUEST_NUMBER']}",
    )
    puts "Saving S3 outputs in env..."
    exportEnvVars({
      KEY_S3_APK_PATH => lane_context[SharedValues::S3_HTML_OUTPUT_PATH],
    })
  end

  desc "Upload HybridApp to Google Play draft mode"
  lane :upload_google_play_draft_hybrid do
    # Google is very unreliable, so we retry a few times
    ENV["SUPPLY_UPLOAD_MAX_RETRIES"]="5"
    upload_to_play_store(
      package_name: ENV["ANDROID_PACKAGE_NAME"],
      json_key: './android-fastlane-json-key.json',
      aab: ENV[KEY_GRADLE_AAB_PATH],
      track: 'internal',
      release_status: 'draft'
    )
  end

  desc "Upload HybridApp to Google Play for internal testing"
  lane :upload_google_play_internal_hybrid do
    # Google is very unreliable, so we retry a few times
    ENV["SUPPLY_UPLOAD_MAX_RETRIES"]="5"
    upload_to_play_store(
      package_name: ENV["ANDROID_PACKAGE_NAME"],
      json_key: './android-fastlane-json-key.json',
      aab: ENV[KEY_GRADLE_AAB_PATH],
      track: 'alpha',
      rollout: '1.0'
    )

    # Update the internal testing group "beta" with the latest version
    upload_to_play_store(
      package_name: ENV["ANDROID_PACKAGE_NAME"],
      json_key: './android-fastlane-json-key.json',
      track: 'alpha',
      track_promote_to: 'beta',
      skip_upload_aab: true
    )

    # Update the internal testing group "Internal Testers" with the latest version
    upload_to_play_store(
      package_name:  ENV["ANDROID_PACKAGE_NAME"],
      json_key: './android-fastlane-json-key.json',
      track: 'alpha',
      track_promote_to: 'Internal Testers',
      skip_upload_aab: true
    )
  end

  desc "Deploy app to Google Play production"
  lane :upload_google_play_production do
    # Google is very unreliable, so we retry a few times
    ENV["SUPPLY_UPLOAD_MAX_RETRIES"]="5"
    google_play_track_version_codes(
      package_name: "com.expensify.chat",
      json_key: './android/app/android-fastlane-json-key.json',
      track: 'internal'
    )
    upload_to_play_store(
      package_name: "com.expensify.chat",
      json_key: './android/app/android-fastlane-json-key.json',
      version_code: ENV["VERSION"].to_i,
      track: 'internal',
      track_promote_to: 'production',
      rollout: '1.0',
      skip_upload_apk: true,
      skip_upload_aab: true,
      skip_upload_metadata: true,
      skip_upload_changelogs: true,
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end

  desc "Deploy HybridApp to Google Play production at 1% rollout"
  lane :upload_google_play_production_hybrid_rollout do
    # Google is very unreliable, so we retry a few times
    ENV["SUPPLY_UPLOAD_MAX_RETRIES"]="5"
    upload_to_play_store(
      package_name: "org.me.mobiexpensifyg",
      json_key: './android-fastlane-json-key.json',
      version_code: ENV["VERSION"].to_i,
      track: 'beta',
      track_promote_to: 'production',
      rollout: '0.01',
      skip_upload_apk: true,
      skip_upload_aab: true,
      skip_upload_metadata: true,
      skip_upload_changelogs: true,
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end

  desc "Submit HybridApp to 100% rollout on Google Play"
  lane :complete_hybrid_rollout do
    # Google is very unreliable, so we retry a few times
    ENV["SUPPLY_UPLOAD_MAX_RETRIES"]="5"
    productionVersionCodes = google_play_track_version_codes(
      track: 'production',
      package_name: "org.me.mobiexpensifyg",
      json_key: './android-fastlane-json-key.json',
    )
    upload_to_play_store(
      package_name: "org.me.mobiexpensifyg",
      json_key: './android-fastlane-json-key.json',
      version_code: productionVersionCodes.sort.last, # Get the latest version code
      track: 'production',
      rollout: '1',
      skip_upload_apk: true,
      skip_upload_aab: true,
      skip_upload_metadata: true,
      skip_upload_changelogs: true,
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end

  desc "Update HybridApp rollout percentage on Google Play"
  lane :update_hybrid_rollout do |options|
    productionVersionCodes = google_play_track_version_codes(
      track: 'production',
      package_name: "org.me.mobiexpensifyg",
      json_key: './android/app/android-fastlane-json-key.json',
    )
    upload_to_play_store(
      package_name: "org.me.mobiexpensifyg",
      json_key: './android/app/android-fastlane-json-key.json',
      version_code: productionVersionCodes.sort.last, # Get the latest version code
      track: 'production',
      rollout: options[:rollout],
      skip_upload_apk: true,
      skip_upload_aab: true,
      skip_upload_metadata: true,
      skip_upload_changelogs: true,
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end
end

def setupIOSSigningCertificate()
  require 'securerandom'
  keychain_password = SecureRandom.uuid

  create_keychain(
    name: "ios-build.keychain",
    password: keychain_password,
    default_keychain: "true",
    unlock: "true",
    timeout: "3600",
    add_to_search_list: "true"
  )

  import_certificate(
    certificate_path: "./Certificates.p12",
    keychain_name: "ios-build.keychain",
    keychain_password: keychain_password
  )
end

platform :ios do
  desc "Build an iOS HybridApp production build"
  lane :build_hybrid do
    ENV["ENVFILE"]="Mobile-Expensify/.env.production.hybridapp.ios"

    setupIOSSigningCertificate()

    install_provisioning_profile(
      path: "./" + ENV["APPLE_STORE_PROVISIONING_PROFILE_FILE"]
    )

    install_provisioning_profile(
      path: "./" + ENV["APPLE_SHARE_PROVISIONING_PROFILE_FILE"]
    )

    install_provisioning_profile(
      path: "./" + ENV["APPLE_NOTIFICATION_PROVISIONING_PROFILE_FILE"]
    )

    update_code_signing_settings(
      path: "Mobile-Expensify/iOS/Expensify.xcodeproj",
      profile_name: ENV["APPLE_STORE_PROVISIONING_PROFILE_NAME"],
      targets: "Expensify",
      bundle_identifier: ENV["APPLE_ID"],
      build_configurations: "Release"
    )

    update_code_signing_settings(
      path: "Mobile-Expensify/iOS/Expensify.xcodeproj",
      profile_name: ENV["APPLE_SHARE_PROVISIONING_PROFILE_NAME"],
      targets: "SmartScanExtension",
      bundle_identifier: ENV["APPLE_ID"] + ".SmartScanExtension",
      build_configurations: "Release"
    )

    update_code_signing_settings(
      path: "Mobile-Expensify/iOS/Expensify.xcodeproj",
      profile_name: ENV["APPLE_NOTIFICATION_PROVISIONING_PROFILE_NAME"],
      targets: "NotificationServiceExtension",
      bundle_identifier: ENV["APPLE_ID"] + ".NotificationServiceExtension",
      build_configurations: "Release"
    )

    build_app(
      workspace: "Mobile-Expensify/iOS/Expensify.xcworkspace",
      scheme: "Expensify",
      output_name: "Expensify.ipa",
      export_method: "app-store",
      export_options: {
        manageAppVersionAndBuildNumber: false,
        provisioningProfiles: {
          ENV["APPLE_ID"] => ENV["APPLE_STORE_PROVISIONING_PROFILE_NAME"],
          ENV["APPLE_ID"] + ".SmartScanExtension" => ENV["APPLE_SHARE_PROVISIONING_PROFILE_NAME"],
          ENV["APPLE_ID"] + ".NotificationServiceExtension" => ENV["APPLE_NOTIFICATION_PROVISIONING_PROFILE_NAME"],
        }
      }
    )

    setIOSBuildOutputsInEnv()
  end

  desc "Build an iOS HybridApp Adhoc build"
  lane :build_adhoc_hybrid do
    ENV["ENVFILE"]="Mobile-Expensify/.env.adhoc.hybridapp.ios"

    setupIOSSigningCertificate()

    install_provisioning_profile(
      path: "./OldApp_AdHoc.mobileprovision"
    )

    install_provisioning_profile(
      path: "./OldApp_AdHoc_Share_Extension.mobileprovision"
    )

    install_provisioning_profile(
      path: "./OldApp_AdHoc_Notification_Service.mobileprovision"
    )

    build_app(
      workspace: "Mobile-Expensify/iOS/Expensify.xcworkspace",
      scheme: "Expensify AdHoc",
      output_name: "Expensify.ipa",
      export_method: "ad-hoc",
      export_options: {
        manageAppVersionAndBuildNumber: false,
        provisioningProfiles: {
          "com.expensify.expensifylite.adhoc" => "(OldApp) AdHoc",
          "com.expensify.expensifylite.adhoc.SmartScanExtension" => "(OldApp) AdHoc: Share Extension",
          "com.expensify.expensifylite.adhoc.NotificationServiceExtension" => "(OldApp) AdHoc: Notification Service",
        }
      }
    )

    setIOSBuildOutputsInEnv()
  end

  desc "Build an unsigned iOS production build"
  lane :build_unsigned do
    ENV["ENVFILE"]=".env.production"
    build_app(
      workspace: "./ios/NewExpensify.xcworkspace",
      scheme: "New Expensify",
      configuration: "Debug",
      sdk: "iphonesimulator",
      skip_codesigning: true,
      skip_archive: true,
      export_method: "development"
    )
    setIOSBuildOutputsInEnv()
  end

  desc "Build an unsigned iOS HybridApp production build"
  lane :build_unsigned_hybrid do
    ENV["ENVFILE"]="./Mobile-Expensify/.env.production.hybridapp.ios"
    build_app(
      workspace: "./Mobile-Expensify/iOS/Expensify.xcworkspace",
      scheme: "Expensify",
      configuration: "Debug",
      sdk: "iphonesimulator",
      skip_codesigning: true,
      skip_archive: true,
      export_method: "development"
    )
    setIOSBuildOutputsInEnv()
  end

  desc "Build AdHoc app for testing"
  lane :build_adhoc do
    ENV["ENVFILE"]=".env.adhoc"

    setupIOSSigningCertificate()

    install_provisioning_profile(
      path: "./NewApp_AdHoc.mobileprovision"
    )

    install_provisioning_profile(
      path: "./NewApp_AdHoc_Notification_Service.mobileprovision"
    )

    install_provisioning_profile(
      path: "./NewApp_AdHoc_Share_Extension.mobileprovision"
    )

    build_app(
      workspace: "./ios/NewExpensify.xcworkspace",
      skip_profile_detection: true,
      scheme: "New Expensify AdHoc",
      export_method: "ad-hoc",
      export_options: {
        method: "ad-hoc",
        provisioningProfiles: {
          "com.expensify.chat.adhoc" => "(NewApp) AdHoc",
          "com.expensify.chat.adhoc.NotificationServiceExtension" => "(NewApp) AdHoc: Notification Service",
          "com.expensify.chat.adhoc.ShareViewController" => "(NewApp) AdHoc: Share Extension",
        },
        manageAppVersionAndBuildNumber: false
      }
    )
    setIOSBuildOutputsInEnv()
  end

  desc "Upload app to S3"
  lane :upload_s3 do
    puts "IPA path: #{ENV[KEY_IPA_PATH]}"
    aws_s3(
      access_key: ENV['S3_ACCESS_KEY'],
      secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
      bucket: ENV['S3_BUCKET'],
      region: ENV['S3_REGION'],
      ipa: ENV[KEY_IPA_PATH],
      app_directory: "ios/#{ENV['PULL_REQUEST_NUMBER']}",
    )
    sh("echo '{\"ipa_path\": \"#{lane_context[SharedValues::S3_IPA_OUTPUT_PATH]}\",\"html_path\": \"#{lane_context[SharedValues::S3_HTML_OUTPUT_PATH]}\"}' > ../ios_paths.json")
  end

  desc "Upload DSYMs to Firebase for HybridApp"
  lane :upload_dsyms do
    puts "dsym path: #{ENV[KEY_DSYM_PATH]}"
    upload_symbols_to_crashlytics(
      app_id: "1:921154746561:ios:216bd10ccc947659027c40",
      dsym_path: ENV[KEY_DSYM_PATH],
      gsp_path: "./ios/GoogleService-Info.plist",
      binary_path: "./ios/Pods/FirebaseCrashlytics/upload-symbols"
    )
  end

  desc "Upload HybridApp to TestFlight"
  lane :upload_testflight_hybrid do
    upload_to_testflight(
      app_identifier: ENV["APPLE_ID"],
      api_key_path: "./ios-fastlane-json-key.json",
      distribute_external: true,
      notify_external_testers: true,
      reject_build_waiting_for_review: true,
      changelog: "Thank you for beta testing New Expensify, this version includes bug fixes and improvements.",
      groups: ["Applause", "Beta Testers", "Expensify Employees"],
      demo_account_required: true,
      beta_app_review_info: {
        contact_email: ENV["APPLE_CONTACT_EMAIL"],
        contact_first_name: "Andrew",
        contact_last_name: "Gable",
        contact_phone: ENV["APPLE_CONTACT_PHONE"],
        demo_account_name: ENV["APPLE_DEMO_EMAIL"],
        demo_account_password: ENV["APPLE_DEMO_PASSWORD"],
        notes: "1. In the Expensify app, enter the email 'appletest.expensify@proton.me'. This will trigger a sign-in link to be sent to 'appletest.expensify@proton.me'\n                  2. Navigate to https://account.proton.me/login, log into Proton Mail using 'appletest.expensify@proton.me' as email and the password associated with 'appletest.expensify@proton.me', provided above\n                  3. Once logged into Proton Mail, navigate to your inbox and locate the email triggered in step 1. The email subject should be 'Your magic sign-in link for Expensify'\n                  4. Open the email and copy the 6-digit sign-in code provided within\n                  5. Return to the Expensify app and enter the copied 6-digit code in the designated login field"
      }
    )
  end

  desc "Upload DSYMs to Firebase for HybridApp"
  lane :upload_dsyms_hybrid do
    puts "dsym path: #{ENV[KEY_DSYM_PATH]}"
    upload_symbols_to_crashlytics(
      app_id: ENV["FIREBASE_HYBRID_APP_ID"],
      dsym_path: ENV[KEY_DSYM_PATH],
      gsp_path: "./ios/GoogleService-Info.plist",
      # Assuming we are running this from the react-native submodule directory for HybridApp
      binary_path: "./Mobile-Expensify/iOS/Pods/FirebaseCrashlytics/upload-symbols"
    )
  end

  desc "Submit HybridApp to 100% rollout on App Store"
  lane :complete_hybrid_rollout do
    # Local path is different when using Spaceship::ConnectAPI::Token.from_json_file,
    # the working directory is: /Users/runner/work/App/App/fastlane
    api_token = Spaceship::ConnectAPI::Token.from_json_file("../ios-fastlane-json-key.json")
    Spaceship::ConnectAPI.token = api_token

    app = Spaceship::ConnectAPI::App.find(ENV["APPLE_ID"])
    version = app.get_live_app_store_version(platform: Spaceship::ConnectAPI::Platform::IOS)

    # Skip if the version is already at 100% rollout
    if version.fetch_app_store_version_phased_release.phased_release_state == "COMPLETE"
      UI.important "Version is already at 100% rollout, skipping completing the rollout"
      next
    end

    version.fetch_app_store_version_phased_release.complete
  end

  desc "Submit HybridApp for production App Store slow rollout"
  lane :submit_hybrid_for_rollout do
    deliver(
      app_identifier: ENV["APPLE_ID"],
      api_key_path: "./ios-fastlane-json-key.json",

      # Skip HTML report verification
      force: true,

      # VERSION will be set to the full build_number e.g. '1.0.92.0'
      build_number: ENV["VERSION"],

      # app_version needs to be set to the short version, without the last digit e.g. '1.0.92'
      app_version: ENV["VERSION"].rpartition(".")[0],

      # We want to submit the version for Apple to review
      submit_for_review: true,

      # We want to release the app as soon as it's approved
      automatic_release: true,

      # We want to enable a slow rollout
      phased_release: true,

      # We need to upload metadata to upload the release notes which is required for each new version
      skip_metadata: false,

      # We do not want to upload any screenshots
      skip_screenshots: true,

      # We do not have any binary to upload as it's already in TestFlight
      skip_binary_upload: true,

      # Reject the current build if there is one in review
      reject_if_possible: true,

      # We do not want to reset the ratings
      reset_ratings: false,

      # Precheck cannot check for in app purchases with the API key we use
      precheck_include_in_app_purchases: false,
      submission_information: {
        # We currently do not use idfa: https://developer.apple.com/app-store/user-privacy-and-data-use/
        add_id_info_uses_idfa: false,

        # We do not need any additional compliance
        export_compliance_compliance_required: false,

        # We do not use any encryption
        export_compliance_encryption_updated: false,
        export_compliance_app_type: nil,
        export_compliance_uses_encryption: false,
        export_compliance_is_exempt: false,
        export_compliance_contains_third_party_cryptography: false,
        export_compliance_contains_proprietary_cryptography: false,

        # We do not show any third party content
        content_rights_contains_third_party_content: false,

        # Indicate that our key has admin permissions
        content_rights_has_rights: true
      },
      release_notes: {
        'en-US' => "Improvements and bug fixes"
      }
    )
  end
end
